﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CartItemEngineTests.cs" company="DST Nexdox">
//   Copyright (c) DST Nexdox. All rights reserved.
// </copyright>
// <summary>
//   Defines the CartItemEngineTests type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace BusinessEngineTests
{
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using BusinessEngineInterfaces;
  using BusinessEngines;
  using Entities;
  using Exceptions;
  using FluentAssertions;
  using Moq;
  using NUnit.Framework;
  using UnityRepository.Interfaces;

  [TestFixture]
  public class CartItemEngineTests
  {
    private Mock<ICartItemRepository> cartItemRepository;
    private Mock<IDocumentRepository> documentRepository;
    private Mock<IManCoRepository> manCoRepository;
    private Mock<ICheckOutRepository> checkOutRepository;
    private Mock<ICheckOutEngine> checkOutEngine;
    private ICartItemEngine cartItemEngine;

    [SetUp]
    public void SetUp()
    {
      this.cartItemRepository = new Mock<ICartItemRepository>();
      this.documentRepository = new Mock<IDocumentRepository>();
      this.checkOutEngine = new Mock<ICheckOutEngine>();
      this.manCoRepository = new Mock<IManCoRepository>();
      this.checkOutRepository = new Mock<ICheckOutRepository>();

      this.cartItemEngine = new CartItemEngine(
        this.cartItemRepository.Object,
        this.documentRepository.Object,
        this.manCoRepository.Object,
        this.checkOutRepository.Object,
        this.checkOutEngine.Object);
    }

    [Test]
    public void GivenDocumentIds_WhenIRequestTheyBeAddedToACart_AndTheyHaveInconsistentManCos_AUnityExceptionIsRaised()
    {
      this.documentRepository.Setup(dr => dr.GetDocuments(It.IsAny<IEnumerable<String>>()))
        .Returns(
          new List<Document>
            {
              new Document { Id = 1, DocumentId = "document1", ManCoId = 1, ManCo = new ManCo { Id = 1, Code = "manco1" } },
              new Document { Id = 2, DocumentId = "document2", ManCoId = 2, ManCo = new ManCo { Id = 2, Code = "manco2" } }
            });

      Action act = () => this.cartItemEngine.AddItems("userName", "cartId", new[] { "documentId1", "documentId2" });

      act.ShouldThrow<UnityException>().WithMessage("Multiple ManCos in document list");
    }

    [Test]
    public void GivenDocumentIds_WhenIRequestTheyBeAddedToACart_TheyAreAddedToTheCart()
    {
      this.documentRepository.Setup(dr => dr.GetDocuments(It.IsAny<IEnumerable<String>>()))
        .Returns(new List<Document> { new Document { Id = 1 }, new Document { Id = 2 } });

      AddToCartResults addToCartResults = this.cartItemEngine.AddItems("userName", "cartId", new[] { "documentId1", "documentId2" });

      this.checkOutRepository.Verify(cor => cor.Create(It.IsAny<IEnumerable<CheckOut>>()), Times.Once);
      this.cartItemRepository.Verify(s => s.Create(It.IsAny<IEnumerable<CartItem>>()), Times.Once());

      addToCartResults.DocumentsAdded.Count.Should().Be(2);
      addToCartResults.DocumentsAlreadyCheckedOut.Count.Should().Be(0);
      addToCartResults.DocumentsWithDifferentManCoToBasket.Count.Should().Be(0);
    }

    [Test]
    public void GivenDocumentIds_WhenIRequestTheyBeAddedToACart_AndOneIsCheckedOut_TheOthersAreAddedToTheCart()
    {
      this.documentRepository.Setup(dr => dr.GetDocuments(It.IsAny<IEnumerable<String>>()))
        .Returns(
          new List<Document>
            {
              new Document { Id = 1, DocumentId = "document1" },
              new Document
                {
                  Id = 2,
                  DocumentId = "document2",
                  CheckOut = new CheckOut { DocumentId = 2 }
                },
              new Document { Id = 3, DocumentId = "document3" }
            });
      
      AddToCartResults addToCartResults = this.cartItemEngine.AddItems("userName", "cartId", new[] { "documentId1", "documentId2", "documentId3" });

      this.checkOutRepository.Verify(cor => cor.Create(It.IsAny<IEnumerable<CheckOut>>()), Times.Once);
      this.cartItemRepository.Verify(s => s.Create(It.IsAny<IEnumerable<CartItem>>()), Times.Once());

      addToCartResults.DocumentsAdded.Count.Should().Be(2);
      addToCartResults.DocumentsAlreadyCheckedOut.Count.Should().Be(1);
      addToCartResults.DocumentsWithDifferentManCoToBasket.Count.Should().Be(0);

      addToCartResults.DocumentsAdded[0].Should().Be("document1");
      addToCartResults.DocumentsAdded[1].Should().Be("document3");

      addToCartResults.DocumentsAlreadyCheckedOut[0].Should().Be("document2");
    }

    [Test]
    public void GivenDocumentIdsOfASingleManCo_WhenIRequestTheyBeAddedToACart_AndTheBasketAlreadyContainsItemsOfADifferentManCo_NoItemsAreAdded()
    {
      this.manCoRepository.Setup(cir => cir.GetManCoForCart(It.IsAny<String>())).Returns(new ManCo { Id = 1, Code = "manco1" });

      this.documentRepository.Setup(dr => dr.GetDocuments(It.IsAny<IEnumerable<String>>()))
        .Returns(
          new List<Document>
            {
              new Document { Id = 1, DocumentId = "document1", ManCoId = 2, ManCo = new ManCo { Id = 2, Code = "manco2" } },
              new Document { Id = 2, DocumentId = "document2", ManCoId = 2, ManCo = new ManCo { Id = 2, Code = "manco2" } }
            });

      AddToCartResults addToCartResults = this.cartItemEngine.AddItems("userName", "cartId", new[] { "documentId1", "documentId2", "documentId3" });

      this.checkOutEngine.Verify(coe => coe.CheckOutDocument(It.IsAny<String>(), It.IsAny<Int32>()), Times.Exactly(0));
      this.cartItemRepository.Verify(s => s.Create(It.Is<IEnumerable<CartItem>>(ci => !ci.Any())), Times.Once);

      addToCartResults.DocumentsAdded.Count.Should().Be(0);
      addToCartResults.DocumentsAlreadyCheckedOut.Count.Should().Be(0);
      addToCartResults.DocumentsWithDifferentManCoToBasket.Count.Should().Be(2);
    }

    [Test]
    public void GivenAGrid_WhenIRequestItBeAddedToACart_TheDocumentsAreAddedToTheCart()
    {
      this.documentRepository.Setup(dr => dr.GetDocumentsIncludingCheckOutEntity(It.IsAny<String>()))
        .Returns(new List<Document> { new Document { Id = 1 }, new Document { Id = 2 } });

      this.checkOutEngine.Setup(c => c.CheckOutDocument(It.IsAny<String>(), It.IsAny<String>())).Returns(new CheckOut { DocumentId = 1 });

      AddToCartResults addToCartResults = this.cartItemEngine.AddGrid("userName", "cartId", "grid1");

      this.checkOutRepository.Verify(cor => cor.Create(It.IsAny<IEnumerable<CheckOut>>()), Times.Once);
      this.cartItemRepository.Verify(s => s.Create(It.IsAny<IEnumerable<CartItem>>()), Times.Once());

      addToCartResults.DocumentsAdded.Count.Should().Be(2);
      addToCartResults.DocumentsAlreadyCheckedOut.Count.Should().Be(0);
      addToCartResults.DocumentsWithDifferentManCoToBasket.Count.Should().Be(0);
    }

    [Test]
    public void GivenAGrid_WhenIRequestItBeAddedToACart_AndADocumentIsCheckedOut_TheOthersAreAddedToTheCart()
    {
      this.documentRepository.Setup(dr => dr.GetDocumentsIncludingCheckOutEntity(It.IsAny<String>()))
        .Returns(
          new List<Document>
            {
              new Document { Id = 1, DocumentId = "document1" },
              new Document
                {
                  Id = 2,
                  DocumentId = "document2",
                  CheckOut = new CheckOut { DocumentId = 2 }
                },
              new Document { Id = 3, DocumentId = "document3" }
            });

      AddToCartResults addToCartResults = this.cartItemEngine.AddGrid("userName", "cartId", "grid1");

      this.checkOutRepository.Verify(cor => cor.Create(It.IsAny<IEnumerable<CheckOut>>()), Times.Once);
      this.cartItemRepository.Verify(s => s.Create(It.IsAny<IEnumerable<CartItem>>()), Times.Once());

      addToCartResults.DocumentsAdded.Count.Should().Be(2);
      addToCartResults.DocumentsAlreadyCheckedOut.Count.Should().Be(1);
      addToCartResults.DocumentsWithDifferentManCoToBasket.Count.Should().Be(0);

      addToCartResults.DocumentsAdded[0].Should().Be("document1");
      addToCartResults.DocumentsAdded[1].Should().Be("document3");

      addToCartResults.DocumentsAlreadyCheckedOut[0].Should().Be("document2");
    }

    [Test]
    public void GivenAGrid_WhenIRequestItBeAddedToACart_AndTheBasketAlreadyContainsItemsOfADifferentManCo_NoItemsAreAdded()
    {
      this.manCoRepository.Setup(cir => cir.GetManCoForCart(It.IsAny<String>())).Returns(new ManCo { Id = 1, Code = "manco1" });

      this.documentRepository.Setup(dr => dr.GetDocumentsIncludingCheckOutEntity(It.IsAny<String>()))
        .Returns(
          new List<Document>
            {
              new Document { Id = 1, DocumentId = "document1", ManCoId = 2, ManCo = new ManCo { Id = 2, Code = "manco2" } },
              new Document { Id = 2, DocumentId = "document2", ManCoId = 2, ManCo = new ManCo { Id = 2, Code = "manco2" } }
            });

      AddToCartResults addToCartResults = this.cartItemEngine.AddGrid("userName", "cartId", "grid1");

      this.checkOutEngine.Verify(coe => coe.CheckOutDocument(It.IsAny<String>(), It.IsAny<Int32>()), Times.Exactly(0));
      this.cartItemRepository.Verify(s => s.Create(It.Is<IEnumerable<CartItem>>(ci => !ci.Any())), Times.Once);

      addToCartResults.DocumentsAdded.Count.Should().Be(0);
      addToCartResults.DocumentsAlreadyCheckedOut.Count.Should().Be(0);
      addToCartResults.DocumentsWithDifferentManCoToBasket.Count.Should().Be(2);
    }
  }
}
